﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OverStore.Configuration;
using System.Data.SqlServerCe;
using OverStore.Configuration.Builder.Fluent;
using OverStore.Configuration.Components;
using System.Data;
using OverStore.Runtime.Storage;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OverStore.Exceptions;
using OverStore.Runtime.PersistenceServiceProvider;
using OverStore.Configuration.Storage;
using OverStore.Configuration.Builder;
using System.Reflection;
using OverStore.Configuration.ObjectRepository.Mapping;
using OverStore.Configuration.ObjectRepository.CommandSet;

namespace OverStore.TutorialAsTest.HelpExamples
{
    [TestClass]
    public class ConfiguringPersister
    {
        /* *****
         * -- Execute following SQL for create database table with data
         * CREATE TABLE SimpleTable (simple_table_id int not null primary key, name nvarchar(50) not null);
         * INSERT INTO SimpleTable VALUES (1, 'First');
         * INSERT INTO SimpleTable VALUES (2, 'Second');
         * INSERT INTO SimpleTable VALUES (3, 'Third');
         *          
         */

        private string CreateDbCopyAndConnectionString()
        {
            return TestHelper.CreateTestDbCopyAndBuildConnectionString();
        }

        public class SimpleClassNoId
        {
            public string Name { get; set; }
        }

        public OverStoreConfiguration CreateSimpleClassNoIdConfiguration(string connectionString)
        {
            return OverStoreFluent.Configure()
                .LogToConsole().ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                .CustomClassRepository<SimpleClassNoId, int>()
                    .DelegateDepot()
                        .CreateInstance(() => new SimpleClassNoId())
                        .DontStoreIdentifier()
                        .Incremental(2000, 1)
                    .EndDepot()
                    .WithMapper(new DelegateMapperConfiguration<SimpleClassNoId>
                                            {
                                                GetObjectDataFunction = (instance, fieldName, persistenceServiceProvider) =>
                                                    {
                                                        var sc = StringComparer.OrdinalIgnoreCase;
                                                        if (sc.Equals(fieldName, "name"))
                                                            return instance.Name;
                                                        if (sc.Equals(fieldName, "simple_table_id"))
                                                            return persistenceServiceProvider.GetIdentifier<SimpleClassNoId, int>(instance);
                                                        return null;
                                                    },
                                                ApplyObjectDataFunction = (data, instance, persistenceServiceProvider) =>
                                                    {
                                                        object value;
                                                        if (data.TryGetValue("name", out value))
                                                            instance.Name = (string)value;

                                                        /* // Uncomment following line if commands return IDENTITY result of simple_table_id column.
                                                           if (data.TryGetValue("simple_table_id", out result))
                                                            persistenceServiceProvider.GetRegistry<SimpleClassNoId, int>().UpdateIdentifier(instance, (int)result);
                                                         */
                                                    }
                                            })
                    .CommandSet()
                        .SelectCommand()
                            .UseSqlCommand("select simple_table_id, name from SimpleTable where simple_table_id = @simple_table_id")
                            .InputParameter("simple_table_id", DbType.Int32)
                        .EndCommand()
                        .InsertCommand()
                            .UseSqlCommand("insert into SimpleTable (simple_table_id, name) values (@simple_table_id, @name)")
                            .InputParameter( "simple_table_id", DbType.Int32)
                            .InputParameter("name", DbType.String )
                        .EndCommand()
                        .UpdateCommand()
                            .UseSqlCommand("update SimpleTable set name = @name where simple_table_id = @simple_table_id")
                            .InputParameter("simple_table_id", DbType.Int32)
                            .InputParameter( "name", DbType.String )
                        .EndCommand()
                        .DeleteCommand()
                              .UseSqlCommand("delete from SimpleTable where simple_table_id = @simple_table_id")
                              .InputParameter("simple_table_id", DbType.Int32)
                         .EndCommand()
                     .EndCommandSet()
                    .NoChangesTracking().NoErrorTransforming()
                    .NoAttachingSupport()
                .EndObjectRepository()
            .End();
        }

        [TestMethod]
        public void SimpleClassNoId_Read()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            using (var session = CreateSimpleClassNoIdConfiguration(connectionString).CreateSession())
            {
                var one = session.GetByIdentifier<SimpleClassNoId, int>(1);
                // IdentifierProvider is not presented in instance data, but anyway we can access 
                // instance by identifier.
                Assert.AreEqual("First", one.Name);
            }
        }

        public class SimpleClass
        {
            public int Id { get; set; }

            public string Name { get; set; }
        }

        public OverStoreConfiguration CreateSimpleClassConfiguration(string connectionString)
        {
            return OverStoreFluent.Configure()
                .LogToConsole().ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                .CustomClassRepository<SimpleClass, int>()
                    .DelegateDepot()
                        .CreateInstance(() => new SimpleClass())
                        .StoreIdentifier((instance, newIdentifier) => { instance.Id = newIdentifier; })
                        .Incremental(2000, 1)
                    .EndDepot()
                    .WithMapper(new DelegateMapperConfiguration<SimpleClass>
                                {
                                    GetObjectDataFunction = (instance, fieldName, persistenceServiceProvider) =>
                                    {
                                        var sc = StringComparer.OrdinalIgnoreCase;
                                        if (sc.Equals(fieldName, "name"))
                                            return instance.Name;
                                        if (sc.Equals(fieldName, "simple_table_id"))
                                            return instance.Id;
                                        // Note here we returns two values: result of Id property that is changed identifier (with name simple_table_id),
                                        // and original identifier result is holded in session.
                                        if (sc.Equals(fieldName, "original_identifier"))
                                            return persistenceServiceProvider.GetIdentifier<SimpleClass, int>(instance);
                                        return null;
                                    },
                                    ApplyObjectDataFunction = (data, instance, persistenceServiceProvider) =>
                                    {
                                        object value;
                                        if (data.TryGetValue("name", out value))
                                            instance.Name = (string)value;
                                        if (data.TryGetValue("simple_table_id", out value))
                                            instance.Id = (int)value;
                                        // Note that we must invoke UpdateIdentifier method in order to syncronize result 
                                        // of Id property with identifier result. The actual syncronization performed by 
                                        // DelegateDepotConfiguration.ApplyIdentifer() method.
                                        if (data.TryGetValue("original_identifier", out value))
                                            persistenceServiceProvider.PutNewIdentifier<SimpleClass, int>(instance, (int)value);
                                    }
                                })
                    .CommandSet()
                        .SelectCommand()
                            .UseSqlCommand( "select simple_table_id, name from SimpleTable where simple_table_id = @original_identifier")
                            .InputParameter("original_identifier", DbType.Int32)
                        .EndCommand()
                        .InsertCommand()
                            .UseSqlCommand("insert into SimpleTable (simple_table_id, name) values (@original_identifier, @name)")
                            .InputParameter("original_identifier", DbType.Int32)
                            .InputParameter("name", DbType.String)
                        .EndCommand()
                        .UpdateCommand()
                            .UseSqlCommand("update SimpleTable set name = @name where simple_table_id = @original_identifier")
                            .InputParameter("original_identifier", DbType.Int32)
                            .InputParameter("name", DbType.String)
                        .EndCommand()
                        .DeleteCommand()
                            .UseSqlCommand("delete from SimpleTable where simple_table_id = @original_identifier")
                            .InputParameter("original_identifier", DbType.Int32)
                        .EndCommand()
                    .EndCommandSet()
                    .ByIdentifierAttaching(i => i.Id)
                    .EndObjectRepository()
                .End();
        }

        [TestMethod]
        public void SimpleClassRead()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            using (var session = CreateSimpleClassConfiguration(connectionString).CreateSession())
            {
                var one = session.GetByIdentifier<SimpleClass, int>(1);
                Assert.AreEqual(1, one.Id);
                Assert.AreEqual("First", one.Name);
            }
        }

        [TestMethod]
        public void SimpleClassReadAfterIdChanging()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            using (var session = CreateSimpleClassConfiguration(connectionString).CreateSession())
            {
                var one = session.GetByIdentifier<SimpleClass, int>(1);
                Assert.AreEqual(1, one.Id);
                Assert.AreEqual("First", one.Name);

                one.Id = 1000;
                // Actual identifier result holded in session is unchanged.
                var one2 = session.GetByIdentifier<SimpleClass, int>(1);
                Assert.AreSame(one, one2);
                // But result of Id property is changed.
                Assert.AreEqual(1000, one2.Id);
            }
        }

        [TestMethod]
        public void SimpleClassAccessibleAfterInstanceDataChanged()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            using (var session = CreateSimpleClassConfiguration(connectionString).CreateSession())
            {
                var one = session.GetByIdentifier<SimpleClass, int>(1);

                // Change values of identifier and name
                one.Id = 1001;
                one.Name = "One thousand and one";

                // ReloadUsingData 
                session.Reload(one);

                // Check instance data restored to it original state
                Assert.AreEqual(1, one.Id);
                Assert.AreEqual("First", one.Name);

                // Note that due reloading command reads data from row with PK = 1,
                // even thought Id property was changed.
            }
        }

        [TestMethod]
        public void SimpleClassSyncIdentifierOnAddNewInstance()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            using (var session = CreateSimpleClassConfiguration(connectionString).CreateSession())
            {
                var newInstance = new SimpleClass { Name = "Two thousand and one" };
                // Now id still has default result 0.
                Assert.AreEqual(0, newInstance.Id);

                session.Add(newInstance);
                // Now result of Id property is syncronized with actual instance identifier.
                // By the way, the result of next identifier for new instance defines by settings
                // of DelegateDepotConfiguration.IdentifierGenerator.
                Assert.AreEqual(2001, newInstance.Id);
            }
        }

        public OverStoreConfiguration CreateSimpleClassConfigurationMutableId(string connectionString)
        {
            return OverStoreFluent.Configure()
                .LogToConsole().ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                .CustomClassRepository<SimpleClass, int>()
                       .DelegateDepot()
                            .CreateInstance(() => new SimpleClass())
                            .StoreIdentifier((instance, newIdentifier) => { instance.Id = newIdentifier; })
                            .Incremental(2000, 1)
                        .EndDepot()
                    .WithMapper(new DelegateMapperConfiguration<SimpleClass>
                                {
                                    GetObjectDataFunction = (instance, fieldName, persistenceServiceProvider) =>
                                    {
                                        var sc = StringComparer.OrdinalIgnoreCase;
                                        if (sc.Equals(fieldName, "name"))
                                            return instance.Name;
                                        if (sc.Equals(fieldName, "simple_table_id"))
                                            return instance.Id;
                                        // Note here we returns two values: result of Id property that is changed identifier (with name simple_table_id),
                                        // and original identifier result is holded in session.
                                        if (sc.Equals(fieldName, "original_identifier"))
                                            return persistenceServiceProvider.GetIdentifier<SimpleClass, int>(instance);
                                        return null;
                                    },
                                    ApplyObjectDataFunction = (data, instance, persistenceServiceProvider) =>
                                    {
                                        object value;
                                        if (data.TryGetValue("name", out value))
                                            instance.Name = (string)value;
                                        if (data.TryGetValue("simple_table_id", out value))
                                            instance.Id = (int)value;
                                        // Note that we must invoke UpdateIdentifier method in order to syncronize result of Id property with identifier result. 
                                        // The actual syncronization performed by DelegateDepotConfiguration.ApplyIdentifer() method.
                                        // Here we use simple_table_id result for identifier, which mapped on SimpleClass.Id property.
                                        if (data.TryGetValue("simple_table_id", out value))
                                            persistenceServiceProvider.PutNewIdentifier<SimpleClass, int>(instance, (int)value);
                                    }
                                })
                    .CommandSet()
                        .SelectCommand()
                            .UseSqlCommand("select simple_table_id, name from SimpleTable where simple_table_id = @original_identifier")
                                .InputParameter("original_identifier", DbType.Int32)
                            .EndCommand()
                        // Immutable identifiers requires using of original identifier in insert command.
                        .InsertCommand()
                            .UseSqlCommand("insert into SimpleTable (simple_table_id, name) values (@simple_table_id, @name)")
                            .InputParameter( "simple_table_id", DbType.Int32)
                            .InputParameter("name", DbType.String)
                        .EndCommand()
                        .UpdateCommand()
                            // Because identifier is mutable, update command uses simple_table_id column contains changed identifier result in SET section,
                            // but original_identifier contains actual identifier result in WHERE section.
                            .UseSqlCommand("update SimpleTable set name = @name, simple_table_id = @simple_table_id where simple_table_id = @original_identifier")
                            .InputParameter("original_identifier", DbType.Int32)
                            .InputParameter("simple_table_id", DbType.Int32)
                            .InputParameter("name", DbType.String)
                        .EndCommand()
                        .DeleteCommand()
                            .UseSqlCommand("delete from SimpleTable where simple_table_id = @original_identifier")
                            .InputParameter("original_identifier", DbType.Int32)
                        .EndCommand()
                    .EndCommandSet()
                    .WithChangesTracker(Component.InstanceChangesTracker.Checksum<SimpleClass, int>("simple_class_id", "name"))
                        .ByIdentifierAttaching(i => i.Id)
                .EndObjectRepository()
            .End();
        }

        [TestMethod]
        public void SimpleClassMutableIdReadAfterIdChanging()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            using (var session = CreateSimpleClassConfigurationMutableId(connectionString).CreateSession())
            {
                var one = session.GetByIdentifier<SimpleClass, int>(1);
                Assert.AreEqual(1, one.Id);
                Assert.AreEqual("First", one.Name);

                one.Id = 2000;
                session.Reload(one);
                // IdentifierProvider result restored to original result
                Assert.AreEqual(1, one.Id);

                // And instance is available by old identifier
                var one2 = session.GetByIdentifier<SimpleClass, int>(1);

                Assert.AreSame(one, one2);
            }
        }

        [TestMethod]
        public void SimpleClassMutableIdNewInstance()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            using (var session = CreateSimpleClassConfigurationMutableId(connectionString).CreateSession())
            {
                var five = new SimpleClass { Id = 5, Name = "Fifth" };
                session.Add(five);
                // On Adding, session generates new identifier 2001 and updates sync instance with identifier
                Assert.AreEqual(2001, five.Id);

                // But if we change identifier after registering instance in session and save the instance, 
                // the new identifier result will be used for inserting and instance identifier will take new result.
                five.Id = 5;
                session.Save(five);
                Assert.AreEqual(5, five.Id);
            }

            // use new session for check database values
            using (var session2 = CreateSimpleClassConfigurationMutableId(connectionString).CreateSession())
            {
                var five2 = session2.GetByIdentifier<SimpleClass, int>(5);
                Assert.AreEqual(5, five2.Id);
                Assert.AreEqual("Fifth", five2.Name);
            }
        }

        [TestMethod]
        [ExpectedException(typeof(PersistenceOperationFailedException))]
        public void SimpleClassMutableIdUpdateIdentifier()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            using (var session = CreateSimpleClassConfigurationMutableId(connectionString).CreateSession())
            {
                var third = session.GetByIdentifier<SimpleClass, int>(3);
                Assert.AreEqual(3, third.Id);
                Assert.AreEqual("Third", third.Name);

                // Change identifier and name and save
                third.Id = 5;
                third.Name = "Fifth";
                session.Save(third);
            }

            using (var session2 = CreateSimpleClassConfigurationMutableId(connectionString).CreateSession())
            {
                // Check that instance with id = 5 and name = Fifth exists
                var fifth = session2.GetByIdentifier<SimpleClass, int>(5);
                Assert.AreEqual(5, fifth.Id);
                Assert.AreEqual("Fifth", fifth.Name);

                // Exception expected, because there is no instance with id = 4 in database.
                var forth = session2.GetByIdentifier<SimpleClass, int>(4);
            }
        }

        /* **************
         * -- Execute following SQL in order to create table with IDENTITY colum
         * create table SimpleTableIdentity
         * (
         *      id int not null primary key identity(1,1),
         *      name nvarchar(100) not null
         * )
         * 
         * insert into SimpleTableIdentity(name) values('One')
         * insert into simpleTableIdentity(name) values('Two')
         * insert into simpleTableIdentity(name) values('Three')
         * 
         * 
         * **************/

        public class SimpleTableIdentity
        {
            public int Id { get; set; }

            public string Name { get; set; }
        }

        public OverStoreConfiguration CreateSimpleTableIdentityConfiguration(string connectionString)
        {

            Func<SimpleTableIdentity, string, IRegistry, object> getObjectData =
                (instance, name, provider) =>
                {
                    var sc = StringComparer.OrdinalIgnoreCase;
                    if (sc.Equals(name, "Id"))
                        return instance.Id;
                    if (sc.Equals(name, "Name"))
                        return instance.Name;
                    if (sc.Equals(name, "original__Id"))
                        return provider.GetIdentifier<SimpleTableIdentity, int>(instance);
                    return Missing.Value;
                };
            Action<TypedRecord, SimpleTableIdentity, IRegistry> applyObjectData =
                (data, instance, provider) =>
                {
                    if (data.ContainsName("Id"))
                        instance.Id = data.GetInt32("Id");
                    instance.Name = data.GetString("Name");
                    if (data.ContainsName("new__Id"))
                    {
                        int id = (int)data.GetDecimal("new__Id");
                        provider.PutNewIdentifier<SimpleTableIdentity, int>(instance, id);
                    }
                };
            return OverStoreFluent.Configure()
                        .LogToConsole()
                        .ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                        .CustomClassRepository<SimpleTableIdentity, int>()
                        .WithDepot(Component.Depot.Delegate<SimpleTableIdentity, int>(() => new SimpleTableIdentity(),
                                                      (instance, identifier) => instance.Id = identifier,
                                                      Component.IdentifierGenerator.Seq<SimpleTableIdentity>(0, -1)))
                        .WithMapper(Component.Mapper.Delegate<SimpleTableIdentity>(getObjectData, applyObjectData))
                        .CommandSet()
                            .SelectCommand()
                                .UseSqlCommand("select * from SimpleTableIdentity where id = @original__Id")
                                .InputParameter("original__Id", DbType.Int32)
                            .EndCommand()
                            .InsertCommand()
                                .UseSqlCommand("insert into SimpleTableIdentity(name) values(@name)")
                                .InputParameter("name", DbType.String)
                                .ThenExecute(ResultComposition.MergePreferSecond)
                                .ExecuteAsReader()
                                .UseSqlCommand("select @@Identity as new__Id")
                            .EndCommand()
                            .UpdateCommand()
                                .UseSqlCommand( "update SimpleTableIdentity set name = @name where id = @original__Id")
                                .InputParameter("original__Id", DbType.Int32)
                                .InputParameter("name", DbType.String)
                            .EndCommand()
                            .DeleteCommand()
                                .UseSqlCommand("delete from SimpleTableIdentity where id = @original__Id")
                                .InputParameter("original__Id", DbType.Int32)
                            .EndCommand()
                        .EndCommandSet()
                        .WithChangesTracker(Component.InstanceChangesTracker.Delegate<SimpleTableIdentity, int, int>(
                                    instance => instance.Id.GetHashCode() + (instance.Name ?? String.Empty).GetHashCode()))
                        .ByIdentifierAttaching(i => i.Id)
                       .EndObjectRepository()
                   .End();
        }

        [TestMethod]
        public void SimpleTableIdentityRead()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            using (var session = CreateSimpleTableIdentityConfiguration(connectionString).CreateSession())
            {
                var first = session.GetByIdentifier<SimpleTableIdentity, int>(1);
                Assert.AreEqual(1, first.Id);
                Assert.AreEqual("One", first.Name);

                first.Id = 6;
                var first1 = session.GetByIdentifier<SimpleTableIdentity, int>(1);
                Assert.AreSame(first, first1);
            }
        }

        [TestMethod]
        public void SimpleTableIdentityAddNew()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            var newItem = new SimpleTableIdentity { Name = "Next" };
            int newItemId = 0;
            using (var session = CreateSimpleTableIdentityConfiguration(connectionString).CreateSession())
            {
                session.Add(newItem);
                // New identifier result is autogenerated by Repository.Depot.IdentifierGenerator
                // And equals to -1
                Assert.AreEqual(-1, newItem.Id);
                session.Save(newItem);
                // Here is some hardcode, I assume that last identifer result was 3 and there are no added rows after it.
                Assert.AreEqual(4, newItem.Id);
                newItemId = newItem.Id;
                newItem.Id = 8;
                // Check that instance still available by old identifier even after instance data was changed.
                var sameItem = session.GetByIdentifier<SimpleTableIdentity, int>(newItemId);
                Assert.AreSame(newItem, sameItem);
            }
            using (var session2 = CreateSimpleTableIdentityConfiguration(connectionString).CreateSession())
            {
                var justAddedItem = session2.GetByIdentifier<SimpleTableIdentity, int>(newItemId);
                Assert.AreNotSame(newItem, justAddedItem); // Just for sure
                Assert.AreEqual(newItemId, justAddedItem.Id);
                Assert.AreEqual("Next", justAddedItem.Name);
            }
        }

        [TestMethod]
        public void DbGeneratedIdentifiersCorrectWorkingOnException()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            var newItem1 = new SimpleTableIdentity { Name = "Next" };
            var newItem2 = new SimpleTableIdentity();
            using (var session = CreateSimpleTableIdentityConfiguration(connectionString).CreateSession())
            {
                session.Add(newItem1);
                session.Add(newItem2);
                try
                {
                    session.SaveAll();
                    Assert.Fail();
                }
                catch (PersistenceOperationFailedException)
                {
                    // Expected exception
                }
                Assert.AreEqual(-1, newItem1.Id);
                Assert.AreEqual(-2, newItem2.Id);
                // newItem1FromSession must be null because 
                var newItem1FromSession = session.GetByIdentifierOrDefault<SimpleTableIdentity, int>(4);
                Assert.IsNull(newItem1FromSession, "Error batch saving does not reset identifier on successfuly saved entities.");
                var newItem2FromSession = session.GetByIdentifierOrDefault<SimpleTableIdentity, int>(5);
                Assert.IsNull(newItem2FromSession, "Error batch saving does not reset identifier on successfuly saved entities.");

                Assert.AreEqual(newItem1, session.GetByIdentifier<SimpleTableIdentity, int>(newItem1.Id));
                Assert.AreEqual(newItem2, session.GetByIdentifier<SimpleTableIdentity, int>(newItem2.Id));
            }
        }

        public OverStoreConfiguration CreateSimpleTableIdentityConfigurationTableBased(string connectionString)
        {
            Func<SimpleTableIdentity, string, IRegistry, object> getObjectData =
                (instance, name, provider) =>
                {
                    var sc = StringComparer.OrdinalIgnoreCase;
                    if (sc.Equals(name, "Id"))
                        return instance.Id;
                    if (sc.Equals(name, "Name"))
                        return instance.Name;
                    if (sc.Equals(name, "orig__Id"))
                        return provider.GetIdentifier<SimpleTableIdentity, int>(instance);
                    return Missing.Value;
                };
            Action<TypedRecord, SimpleTableIdentity, IRegistry> applyObjectData =
                (data, instance, provider) =>
                {
                    if (data.ContainsName("Id"))
                        instance.Id = data.GetInt32("Id");
                    instance.Name = data.GetString("Name");
                    if (data.ContainsName("new__Id"))
                    {
                        int id = (int)data.GetDecimal("new__Id");
                        provider.PutNewIdentifier<SimpleTableIdentity, int>(instance, id);
                    }
                };
            return OverStoreFluent.Configure()
                        .LogToConsole()
                        .ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                        .CustomClassRepository<SimpleTableIdentity, int>()
                        .WithDepot(Component.Depot.Delegate<SimpleTableIdentity, int>(() => new SimpleTableIdentity(),
                                                      (instance, identifier) => instance.Id = identifier,
                                                      Component.IdentifierGenerator.Seq<SimpleTableIdentity>(0, -1)))
                        .WithMapper(Component.Mapper.Delegate<SimpleTableIdentity>(getObjectData, applyObjectData))
                        .CommandsFromTable(false,
                            Component.CreateTable("SimpleTableIdentity")
                                .DbGeneratedKey<int>("Id")
                                .Column<string>("Name"))
                        .WithChangesTracker(Component.InstanceChangesTracker.Delegate<SimpleTableIdentity, int, int>(
                                    instance => instance.Id.GetHashCode() + (instance.Name ?? String.Empty).GetHashCode()))
                        .ByIdentifierAttaching(i => i.Id)
                       .EndObjectRepository()
                   .End();
        }

        [TestMethod]
        public void TableBasedSimpleTableIdentityAddNew()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            var newItem = new SimpleTableIdentity { Name = "Next" };
            int newItemId = 0;
            using (var session = CreateSimpleTableIdentityConfigurationTableBased(connectionString).CreateSession())
            {
                session.Add(newItem);
                // New identifier result is autogenerated by Repository.Depot.IdentifierGenerator
                // And equals to -1
                Assert.AreEqual(-1, newItem.Id);
                session.Save(newItem);
                // Here is some hardcode, I assume that last identifer result was 3 and there are no added rows after it.
                Assert.AreEqual(4, newItem.Id);
                newItemId = newItem.Id;
                newItem.Id = 8;
                // Check that instance still available by old identifier even after instance data was changed.
                var sameItem = session.GetByIdentifier<SimpleTableIdentity, int>(newItemId);
                Assert.AreSame(newItem, sameItem);
            }
            using (var session2 = CreateSimpleTableIdentityConfigurationTableBased(connectionString).CreateSession())
            {
                var justAddedItem = session2.GetByIdentifier<SimpleTableIdentity, int>(newItemId);
                Assert.AreNotSame(newItem, justAddedItem); // Just for sure
                Assert.AreEqual(newItemId, justAddedItem.Id);
                Assert.AreEqual("Next", justAddedItem.Name);
            }
        }

        public OverStoreConfiguration CreateSimpleTableIdentityConfigurationTableMapping(string connectionString)
        {
            return OverStoreFluent.Configure()
                        .LogToConsole()
                        .ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                        .MapClassToTable<SimpleTableIdentity, int>("SimpleTableIdentity")
                            .MapDbGeneratedKey("Id", e => e.Id, (e, id) => e.Id = id, Component.IdentifierGenerator.Seq<SimpleTableIdentity>(0, -1))
                            .MapScalarValue<string>("Name", e => e.Name, (e, v) => e.Name = v)
                        .EndTableMapping()
                   .End();
        }

        [TestMethod]
        public void TableMappingSimpleTableIdentityAddNew()
        {
            var connectionString = CreateDbCopyAndConnectionString();
            var newItem = new SimpleTableIdentity { Name = "Next" };
            int newItemId = 0;
            using (var session = CreateSimpleTableIdentityConfigurationTableMapping(connectionString).CreateSession())
            {
                session.Add(newItem);
                // New identifier result is autogenerated by Repository.Depot.IdentifierGenerator
                // And equals to -1
                Assert.AreEqual(-1, newItem.Id);
                session.Save(newItem);
                // Here is some hardcode, I assume that last identifer result was 3 and there are no added rows after it.
                Assert.AreEqual(4, newItem.Id);
                newItemId = newItem.Id;
                newItem.Id = 8;
                // Check that instance still available by old identifier even after instance data was changed.
                var sameItem = session.GetByIdentifier<SimpleTableIdentity, int>(newItemId);
                Assert.AreSame(newItem, sameItem);
            }
            using (var session2 = CreateSimpleTableIdentityConfigurationTableMapping(connectionString).CreateSession())
            {
                var justAddedItem = session2.GetByIdentifier<SimpleTableIdentity, int>(newItemId);
                Assert.AreNotSame(newItem, justAddedItem); // Just for sure
                Assert.AreEqual(newItemId, justAddedItem.Id);
                Assert.AreEqual("Next", justAddedItem.Name);
            }
        }
    }
}
